package gxprod.androidai;

import gxprod.androidai.Bubble.BubbleType;
import gxprod.androidai.functions.CallFunction;
import gxprod.androidai.functions.ConversionFunction;
import gxprod.androidai.functions.Function;
import gxprod.androidai.functions.InteractionFunction;
import gxprod.androidai.functions.MathFunction;
import gxprod.androidai.functions.MemoryFunction;
import gxprod.androidai.functions.OrganizationFunction;
import gxprod.androidai.functions.QuestionSet1Function;
import gxprod.androidai.functions.SMSFunction;
import gxprod.androidai.functions.SearchFunction;
import gxprod.androidai.functions.WeatherFunction;
import gxprod.androidai.regex.MatchResult;

import java.util.Collections;
import java.util.Vector;

import android.util.Log;

/**
 * Manages all the functions and locates the most accurate match for the current statement.
 * Also processes the statement and simplifies it a bit ("an" is changed  to "a", "how is" to "hows", etc)
 * @author Josh Maleszewski
 */
public class FunctionManager {

	private static String[] olds = { "!", "?", " an ", "how is", "where is", "when is", "what is", "whom", "'", };
	private static String[] news = { ".", ".", " a ",  "hows",   "wheres",   "whens",   "whats",   "who",  "",  };

	private static String raw;
	private static boolean IsTyped;

	public Vector<Function> Functions;
	public Vector<Operation> Operations;
	public Vector<QueryAccepter> QueryAccepters;

	public BubbleAdapter Adapter;
	public AIActivity Activity;
	/**
	 * When eve (this ai) asks the user a question, this is set with responses based upon the users answer.
	 * This will need to be implemented.
	 */
	public Question request;

	public FunctionManager(AIActivity activity) {
		Activity = activity;
		Adapter = activity.adapter;
		Functions = new Vector<Function>();
		Operations = new Vector<Operation>();
		QueryAccepters = new Vector<QueryAccepter>();
		populateFunctionList();
	}

	private void populateFunctionList() {
		Functions.clear();
		// !!!! All functions must be registered here to be used !!!!

		Functions.add(new WeatherFunction());
		Functions.add(new SMSFunction());
		Functions.add(new SearchFunction());
		Functions.add(new ConversionFunction());
		Functions.add(new CallFunction());
		Functions.add(new OrganizationFunction());
		Functions.add(new InteractionFunction());
		Functions.add(new MemoryFunction());
		Functions.add(new MathFunction());
		Functions.add(new QuestionSet1Function());
		return;
	}

	public String processStatement(String input) {
		input = (" " + input + " ");//.toLowerCase();
		for(int i = 0; i < olds.length; i++) 
			input = input.replace(olds[i], news[i]);

		return input.trim();
	}


	public void cleanUpOperations() {
		for(int i = 0; i < Operations.size(); i++)
			if(Operations.get(i).isCompleated) {
				Operations.remove(i);
				i--;
			}
		if(Operations.size() == 0)
			AIActivity.get().TurnOffSpinner();
	}


	public boolean executeVoiceFunction(Object[] cmds) {

		IsTyped = (cmds.length == 1);

		String[] commands = new String[cmds.length];
		for(int k = 0; k < cmds.length; k++)
			commands[k] = cmds[k].toString();

		for(int i = 0; i < commands.length; i++) {
			raw = commands[i];

			if(request == null) {

				commands[i] = processStatement(commands[i]);
				Vector<MatchResult> matches = new Vector<MatchResult>();

				for(int j = 0; j < QueryAccepters.size(); j++) {
					QueryAccepter qa = QueryAccepters.get(j);
					MatchResult res = qa.check(commands[i]);
					if(res.Confidence > 0)
						matches.add(res);
				}
				
				for(int j = 0; j < Functions.size(); j++) {
					Function f = Functions.get(j);
					if(f.cullFunction(commands[i])) {
						MatchResult res = f.isMatch(commands[i]);
						if(res.Confidence > 0)
							matches.add(res);						
					}
				}

				if(matches.size() == 0) 
					continue;

				boolean sucess = executeMatches(matches);
				if(sucess) {
					cleanUpOperations();
					QueryAccepters.clear();
					return true;
				}
			} 
			else {
				if(commands[i].equals("never mind") | commands[i].equals("nevermind") | commands[i].equals("forget it")) {
					// TODO: remove questions
					Adapter.output(request.onCancelSay() + "Is there anything else i can help you with?");
					request = null;
					return true;
				} else
					request.process(commands[i], Adapter);
			}
			Adapter.notifyDataSetChanged();
		}
		cleanUpOperations();

		Adapter.output("Hu?");
		Adapter.getArray().insertElementAt(new Bubble(raw, BubbleType.input), Adapter.getCount() - 1);
		return false;
	}

	public boolean executeMatches(Vector<MatchResult> matches) {

		Collections.sort(matches);

		int index = 0, beforeCount = Adapter.getCount();
		boolean sucess = false;

		while(!sucess & index < matches.size()) {
			sucess = matches.get(index).Function.execute(matches.get(index), Adapter);
			index++;
		}
		if(sucess) {
			index--;
			Adapter.getArray().insertElementAt(new Bubble(raw, BubbleType.input), beforeCount);
			Log.d("","Match Found: '" + matches.get(index).Function.getClass().getName() + "'   Index: " + matches.get(index).MatchIndex);
		}
		return sucess;
	}



	public void beginOperation(Operation.ParmedRunnable runner) {
		Operation op = new Operation(runner);
		Operations.add(op);
		Activity.TurnOnSpinner();
	}
	public void beginOperation(Operation.ParmedRunnable runner, final MatchResult match, final BubbleAdapter adapter) {
		final Operation op = new Operation(runner);
		Operations.add(op);
		new Thread(new Runnable() {
			public void run() {
				op.execute(match, null, adapter);
				cleanUpOperations();
			}
		}).start();
		Activity.TurnOnSpinner();
	}


	public void askQuestion(Question question) {
		request = question;
		Adapter.output(question.Query);
	}
	public void askQuestion(Question question, String say) {
		request = question;
		Adapter.output(question.Query, say, BubbleType.question);
	}


	public static boolean isTyped() { return IsTyped; }
	public static boolean isVoice() { return !IsTyped; }

	public static String getRawInput() { return raw; }
}